package com.imooc.infra.repository.jdbc.table;


import com.imooc.business.domain.Metadata;
import com.imooc.business.domain.Rule;
import com.imooc.business.domain.Storage;
import com.imooc.infra.repository.jdbc.params.StandardSqlParam;
import com.imooc.module.collect.enums.DataTypeEnum;
import com.imooc.module.collect.enums.DimensionEnum;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Tim
 * @description: 构建 建表和修改表结构sql
 */
@Component
public class DynamicTableExecute implements TableExecute {

    /**
     * 建表语句
     * @param rule
     * @param metadatas
     * @return
     */
    @Override
    public List<String> addTableSQL(Rule rule, List<Metadata> metadatas) {
        return Arrays.stream(DimensionEnum.values())
                .map(dimension -> Storage.DEFAULT_DATA_SUFFIX.stream()
                        .map(suffix -> {
                            String tableName = rule.getStorage().getTableName(rule.getEnName(), dimension, suffix);
                            return doBuildCreateTableSQL(tableName, metadatas, dimension);
                        }).flatMap(Collection::stream)
                        .collect(Collectors.toList()))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }

    /**
     * 新增字段sql语句
     * @param rule
     * @param metadatas
     * @return
     */
    @Override
    public List<String> updateTableSQL(Rule rule, List<Metadata> metadatas) {
        return Arrays.stream(DimensionEnum.values())
                .map(dimension -> Storage.DEFAULT_DATA_SUFFIX.stream()
                        .map(suffix -> {
                            String tableName = rule.getStorage().getTableName(rule.getEnName(), dimension, suffix);
                            return buildUpdateTableSQL(tableName, metadatas);
                        })
                        .collect(Collectors.toList()))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }

    /**
     * 新增字段sql
     * @param tableName
     * @param addMetadataList
     * @return
     */
    public String buildUpdateTableSQL(String tableName, List<Metadata> addMetadataList) {
        String modifySql = addMetadataList.stream().map(meta -> buildUpdateColumn(tableName, meta.getEnName(), meta.getDataType().getColumnType())).collect(Collectors.joining(""));
        return modifySql;
    }

    /**
     * 真正创建表的sql语句
     * @param tableName
     * @param metadataList
     * @param dimension
     * @return
     */
    private List<String> doBuildCreateTableSQL(String tableName, List<Metadata> metadataList, DimensionEnum dimension) {
        List<String> sqlList = Lists.newArrayList();
        StringBuilder createTableSQL = new StringBuilder();
        createTableSQL.append("CREATE TABLE ").append(tableName).append(" (\n");
        createTableSQL.append(buildIdColumn("主键ID"));
        createTableSQL.append(buildColumn("rule_id", "bigint(20)", false, true));
        createTableSQL.append(buildColumn(StandardSqlParam.COUNT_TIME, "varchar(64)", false, true));
        if (DimensionEnum.USER.equals(dimension)) {
            createTableSQL.append(buildColumn("user_id", "bigint(20)", false, true));
            createTableSQL.append(buildColumn("group_id", "varchar(64)", false, true));
        }
        if (DimensionEnum.GROUP.equals(dimension)) {
            createTableSQL.append(buildColumn("group_id", "varchar(64)", false, true));
        }
        createTableSQL.append(buildColumn("school_id", "bigint(20)", false, true));
        String customizeColumn = metadataList.stream().map(meta -> buildColumn(meta.getEnName(), meta.getDataType().getColumnType(), false, true)).collect(Collectors.joining(""));
        createTableSQL.append(customizeColumn);
        createTableSQL.append(buildColumn("gmt_create", "datetime", false, true));
        createTableSQL.append(buildColumn("gmt_modified", "timestamp", false, true));
        createTableSQL.append(buildColumn("status", "tinyint(4)", false, true))
                .append("PRIMARY KEY (id)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;\n");

        sqlList.add(createTableSQL.toString());
        sqlList.add(buildIndexSQL(tableName, dimension));
        return sqlList;
    }

    /**
     * 构建索引
     * @param tableName
     * @param dimension
     * @return
     */
    public String buildIndexSQL(String tableName, DimensionEnum dimension) {
        StringBuilder indexSQL = new StringBuilder();
        switch (dimension){
            case USER -> indexSQL.append(buildIndex(tableName, StandardSqlParam.COUNT_TIME, "school_id", "group_id", "user_id"));
            case GROUP -> indexSQL.append(buildIndex(tableName, StandardSqlParam.COUNT_TIME, "school_id", "group_id"));
            case SCHOLL -> indexSQL.append(buildIndex(tableName, StandardSqlParam.COUNT_TIME, "school_id"));
        }
        return indexSQL.toString();
    }

    private String buildColumn(String column, String columnType, boolean allowNull, boolean addComma) {
        DataTypeEnum dataTypeEnum = DataTypeEnum.parseColumn(columnType);
        String dbDefaultValue = dataTypeEnum.getDbDefaultValue();
        return column + " " + columnType + (allowNull ? " " : " NOT NULL ") + (StringUtils.isNotEmpty(dbDefaultValue) ? "DEFAULT "+ dbDefaultValue : "") + (addComma ? ",\n" : "");
    }

    private String buildIdColumn(String comment) {
        return "id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '"+comment+"',\n";
    }

    private String buildIndex(String tableName, String... columns) {
        return "CREATE INDEX " + String.join("_", columns) + "_idx ON " + tableName + "(" + String.join(",", columns) + ");\n";
    }

    private String buildUpdateColumn(String tableName, String column, String columnType) {
        return "ALTER TABLE " + tableName + "\" ADD COLUMN \"" + column + "\" " + columnType + " NOT NULL;\n";
    }
}
